package com.gitee.srurl.core.service.impl;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gitee.srurl.core.domain.SusApiUserDomain;
import com.gitee.srurl.core.domain.SusUserDomain;
import com.gitee.srurl.core.dto.UpdatePasswordDTO;
import com.gitee.srurl.core.exception.ServiceException;
import com.gitee.srurl.core.mapper.SusApiUserMapper;
import com.gitee.srurl.core.mapper.SusUserMapper;
import com.gitee.srurl.core.properties.IdGenProperties;
import com.gitee.srurl.core.service.ISusUserService;
import com.gitee.srurl.core.util.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.Optional;

/**
 * @author zxw
 * @date 2023/5/2 22:29
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SusUserServiceImpl extends ServiceImpl<SusUserMapper, SusUserDomain> implements ISusUserService {

    private final SusApiUserMapper apiUserMapper;

    private final IdGenProperties idGenProperties;

    @Override
    public IPage<SusUserDomain> getUserList(PageQuery pageQuery, SusUserDomain userDomain) {

        LambdaQueryWrapper<SusUserDomain> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(StrUtil.isNotBlank(userDomain.getNickname()), SusUserDomain::getNickname, userDomain.getNickname())
                .like(StrUtil.isNotBlank(userDomain.getUsername()), SusUserDomain::getUsername, userDomain.getUsername())
                .eq(StrUtil.isNotBlank(userDomain.getStatus()), SusUserDomain::getStatus, userDomain.getStatus())
                .eq(StrUtil.isNotBlank(userDomain.getApiUser()), SusUserDomain::getApiUser, userDomain.getApiUser());

        Page<Object> page = pageQuery.build();
        return this.getBaseMapper().queryPage(page, queryWrapper);
    }

    @Override
    public SusUserDomain getUserInfo(Long userId) {

        return this.getBaseMapper().selectInfoById(userId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateUser(SusUserDomain userDomain) {

        SusUserDomain historyUserDomain = this.getById(userDomain.getId());
        if (historyUserDomain == null) {
            throw new ServiceException("未查询到对应的用户信息！");
        }

        if (StrUtil.isNotBlank(userDomain.getPassword())) {
            historyUserDomain.setPassword(CryptoUtil.passwordCrypto(userDomain.getPassword()));
        }
        historyUserDomain.setNickname(userDomain.getNickname());
        historyUserDomain.setStatus(userDomain.getStatus());

        // 如果是API用户 查询扩展信息
        if (StrUtil.equals(userDomain.getApiUser(), "1")) {
            LambdaQueryWrapper<SusApiUserDomain> apiUserQueryWrapper = new LambdaQueryWrapper<>();
            apiUserQueryWrapper.eq(SusApiUserDomain::getUserId, userDomain.getId());
            SusApiUserDomain susApiUserDomain = apiUserMapper.selectOne(apiUserQueryWrapper);
            if (susApiUserDomain == null) {

                int allowUrlLength = this.getAllowUrlLength(userDomain);
                // 设置扩展用户信息
                susApiUserDomain = SusApiUserDomain.builder()
                        .id(UniqueIdUtil.nextId())
                        .userId(userDomain.getId())
                        .allowUrlLength(allowUrlLength)
                        // 因为可能已经生成ID 设置会导致判重出问题 所以旧用户不设置 只会在创建用户的时候添加
                        .cacheRandom("")
                        .build();

                apiUserMapper.insert(susApiUserDomain);
            } else {
                susApiUserDomain.setAllowUrlLength(this.getAllowUrlLength(userDomain));
                apiUserMapper.updateById(susApiUserDomain);
            }
        }

        this.updateById(historyUserDomain);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addUser(SusUserDomain userDomain) {

        userDomain.setId(null);
        userDomain.setPassword(CryptoUtil.passwordCrypto(userDomain.getPassword()));
        if (StrUtil.equals(userDomain.getApiUser(), "1")) {
            userDomain.setApiKey(MurmurHashUtil.murmur128(IdUtil.fastSimpleUUID()));
        } else {
            userDomain.setApiKey(null);
        }
        this.save(userDomain);

        if (StrUtil.equals(userDomain.getApiUser(), "1")) {
            // 设置扩展用户信息
            SusApiUserDomain apiUserDomain = SusApiUserDomain.builder()
                    .id(UniqueIdUtil.nextId())
                    .userId(userDomain.getId())
                    .allowUrlLength(this.getAllowUrlLength(userDomain))
                    // 取8位
                    .cacheRandom(IdUtil.fastSimpleUUID().substring(0, 8))
                    .build();

            apiUserMapper.insert(apiUserDomain);
        }
    }

    @Override
    public void updatePassword(Long userId, UpdatePasswordDTO updatePasswordDTO) {

        String password = RsaUtil.decrypt(updatePasswordDTO.getPassword());
        String newPassword = RsaUtil.decrypt(updatePasswordDTO.getNewPassword());
        if (StrUtil.equals(password, newPassword)) {
            throw new ServiceException("新旧密码不能重复！");
        }
        if (newPassword.length() < 8) {
            throw new ServiceException("新密码长度不能低于8位");
        }
        SusUserDomain userDomain = this.getById(userId);
        if (userDomain == null) {
            log.error("用户:{},查询失败！", userId);
            throw new ServiceException("用户信息异常！");
        }
        if (!StrUtil.equals(userDomain.getStatus(), "1")) {
            log.info("用户:{},账号禁用，不允许修改密码.", userId);
            throw new ServiceException("用户已被禁用，不允许进行操作！");
        }

        boolean matches = CryptoUtil.passwordMatches(password, userDomain.getPassword());
        if (!matches) {
            throw new ServiceException("原密码错误，请确认密码信息！");
        }

        userDomain.setPassword(CryptoUtil.passwordCrypto(newPassword));
        userDomain.setUpdateId(userId);
        userDomain.setUpdateTime(LocalDateTime.now());
        this.updateById(userDomain);
    }

    private int getAllowUrlLength(SusUserDomain userDomain) {
        Integer defaultMaxUrlLength = Optional.ofNullable(
                idGenProperties.getDoubleDuplicateDetection().getDefaultMaxUrlLength()
        ).orElse(128);
        return Optional.ofNullable(userDomain.getAllowUrlLength()).orElse(defaultMaxUrlLength);
    }
}
